package jp.co.nes.awssupport.common.utility;

import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.stream.Collectors;

import org.apache.commons.lang3.StringUtils;

import jp.co.nes.awssupport.common.base.CommonException;

/**
 * プロパティ情報に関するユーティリティです。<br>
 * 以下の方法でプロパティ情報を読み込みます。
 * <p>
 * ■Lambda関数でない場合<br>
 * システムプロパティの内容 + システムプロパティ(PropertyNameキー)に設定されているプロパティファイル名(設定されていない場合は、
 * application.properties)をクラスパスのルートから取得し、読み込みます。<br>
 * なお、システムプロパティ(PropertyNameキー)に設定されているプロパティファイルの内容に、システムプロパティの内容と同じキー名が存在した場合、
 * システムプロパティの内容が優先されます。
 * </p>
 * <p>
 * ■Lambda関数の場合<br>
 * Lambda関数に設定されている環境情報の内容 + Lambda関数に設定されている環境情報(PropertyNameキー)に設定されている
 * プロパティファイル名(設定されていない場合は、application.properties)をクラスパスのルートから取得し、読み込みます。<br>
 * ただし、AppInfoPrefixOfLambdaEnvキーに設定されている値がプリフィックスになっているプロパティは、除外されます。<br>
 * AppInfoPrefixOfLambdaEnvキーに設定する値は、「アプリケーション情報のカテゴリ + アプリケーション情報の番号 + "_"」形式で設定します。<br>
 * また、Lambda関数に設定されている環境情報(PropertyNameキー)に設定されているプロパティファイルの内容に、
 * Lambda関数に設定されている環境情報の内容と同じキー名が存在した場合、Lambda関数に設定されている環境情報の内容が優先されます。
 * </p>
 */
public class PropertiesUtil {

    /** プロパティ */
    private static Properties properties;

    /**
     * 初期化処理です。
     */
    static {
        String propertyNameKey = "PropertyName";
        properties = new Properties();
        try {
            String propertyName = null;
            String lambdaFunctionName = System.getenv("AWS_LAMBDA_FUNCTION_NAME");
            if (lambdaFunctionName == null) {
                // Lambda関数でない場合
                propertyName = System.getProperty(propertyNameKey, "application.properties");
            } else {
                // Lambda関数の場合
                propertyName = System.getenv(propertyNameKey);
                if (StringUtils.isEmpty(propertyName)) {
                    propertyName = "application.properties";
                }
            }

            if (!StringUtils.isEmpty(propertyName)) {
                InputStream inputStrem = ClassLoader.getSystemResourceAsStream(propertyName);
                if (inputStrem != null) {
                    properties.load(inputStrem);
                }
            }

            if (lambdaFunctionName == null) {
                // Lambda関数でない場合
                properties.putAll(System.getProperties());
            } else {
                // Lambda関数の場合
                properties.putAll(System.getenv());

                String prefixValue = properties.getProperty("AppInfoPrefixOfLambdaEnv");
                if (!StringUtils.isEmpty(prefixValue)) {
                    // AppInfoPrefixOfLambdaEnvキーに設定されている値がプリフィックスになっているプロパティは除外する。
                    String[] prefixArray = prefixValue.split(",");
                    Map<String, String> workMap = new HashMap<>();
                    Set<Object> keySet = properties.keySet();
                    for (Object key : keySet) {
                        workMap.put((String) key, properties.getProperty((String) key));
                    }
                    for (String prefix : prefixArray) {
                        List<String> keyList = workMap.keySet().stream().filter(key -> {
                            return !StringUtils.isEmpty(key) && key.startsWith(prefix);
                        }).collect(Collectors.toList());

                        for (String key : keyList) {
                            properties.remove(key);
                        }
                    }
                }
            }
        } catch (IOException e) {
            throw new CommonException(e);
        }
    }

    /**
     * デフォルトコンストラクタです。<br>
     * 本クラスはインスタンスを生成しません。
     */
    private PropertiesUtil() {
    }

    /**
     * プロパティからキーに対応する値を返却します。
     *
     * @param key キー
     * @return 値
     */
    public static String getProperty(String key) {
        return properties.getProperty(key);
    }

    /**
     * プロパティからキーに対応する値を返却します。<br>
     * 値が存在しなかった場合はデフォルトを返却します。
     *
     * @param key キー
     * @param defaultValue デフォルト
     * @return 値
     */
    public static String getProperty(String key, String defaultValue) {
        return properties.getProperty(key, defaultValue);
    }

    /**
     * プロパティに対してキーに対応する値を設定します。
     *
     * @param key キー
     * @param value 値
     */
    public static void setProperty(String key, String value) {
        properties.setProperty(key, value);
    }

    /**
     * プロパティを削除します。
     *
     * @param key キー
     */
    public static void removeProperty(String key) {
        properties.remove(key);
    }
}
